﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows.Forms;
using LoLTimers.DataObjects;
using LoLTimers.Utilities;
using Timer = System.Timers.Timer;
using System.Speech.Synthesis;

namespace LoLTimers
{
    public partial class TimerDisplay : Form
    {
        #region FormMovementDllImportsAndConsts

        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;
        public const int GWL_EXSTYLE = -20;
        public const int WS_EX_LAYERED = 0x80000;
        public const int WS_EX_TRANSPARENT = 0x20;
        public const int LWA_ALPHA = 0x2;
        public const int LWA_COLORKEY = 0x1;

        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        [DllImport("user32.dll")]
        public static extern bool ReleaseCapture();
        [DllImport("user32.dll", SetLastError = true)]
        private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
        [DllImport("user32.dll")]
        static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

        private void Move_MouseDown(object sender, MouseEventArgs e)
        {
            if (Control.ModifierKeys == Keys.Control)
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
                    _tdo.Bounds = Bounds;
                }
            }

        }
        #endregion

        private TimerDataObject _tdo;
        private DateTime _respawnTime;
        private Timer _noticeMe = new Timer(1000f);
        private bool isUp = true;
        
        //For future development
        //private SpeechSynthesizer speaker = new SpeechSynthesizer();
        //private bool isSpeaking = false;

        public TimerDataObject TimerData
        {
            get { return _tdo; }
        }

        public string ID
        {
            get { return _tdo.ID; }
        }

        public TimerDisplay(TimerDataObject timerDataObject)
        {
            InitializeComponent();
            
            SetWindowLong(this.Handle, GWL_EXSTYLE, (IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
            SetLayeredWindowAttributes(this.Handle, 0, 128, LWA_ALPHA);
            
            _tdo = timerDataObject;
            
            _respawnTime = new DateTime();

            _noticeMe.Elapsed += new System.Timers.ElapsedEventHandler(_noticeMe_Elapsed);

            this.Bounds = _tdo.Bounds;
            this.BackColor = _tdo.BackgroundColor;

            //For future development
            //speaker.Rate = 1;
            //speaker.Volume = 100;
            //speaker.SpeakCompleted += new EventHandler<SpeakCompletedEventArgs>(speaker_SpeakCompleted);
            
            UpdateFont(_tdo.TimerFont, _tdo.ForeColor);
            UpdateText(_tdo.Text);
            UpdateImage(_tdo.DisplayImage);
        }
        
        public void SetActive(bool isActive)
        {
            if (isActive)
            {
                if (FormBorderStyle == FormBorderStyle.None)
                {
                    SetWindowLong(this.Handle, GWL_EXSTYLE, (IntPtr)(GetWindowLong(this.Handle, 0)));
                    SetLayeredWindowAttributes(this.Handle, 0, 255, LWA_ALPHA);
                }
            }
            else
            {
                if (FormBorderStyle == FormBorderStyle.None)
                {
                    SetWindowLong(this.Handle, GWL_EXSTYLE,
                                  (IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
                    SetLayeredWindowAttributes(this.Handle, 0, 128, LWA_ALPHA);
                }
            }
        }

        private void lblDisplayText_MouseDown(object sender, MouseEventArgs e)
        {
            if ((Control.ModifierKeys & Keys.Alt) != 0)
            {
                if (e.Button == MouseButtons.Right)
                    FormBorderStyle = FormBorderStyle.SizableToolWindow;
                else if (e.Button == MouseButtons.Left)
                    FormBorderStyle = FormBorderStyle.None;
            }
            else if (Control.ModifierKeys == Keys.Shift)
            {
                if (e.Button == MouseButtons.Left)
                    StartTimer();
                else
                    StopTimer();
            }
        }

        void _noticeMe_Elapsed(object sender, ElapsedEventArgs e)
        {
            Blink();
        }

        public void StartTimer()
        {
            _respawnTime = DateTime.Now.AddSeconds(_tdo.GameData.CooldownSeconds);
            isUp = false;
        }

        public void StopTimer()
        {
            isUp = true;
            _respawnTime = DateTime.Now;
        }

        public void ToggleTimer()
        {
            if(isUp)
                StartTimer();
            else
                StopTimer();
        }

        public void UpdateTimer()
        {
            EnsureOnTop();
            TimeSpan tmpTimeSpan = _respawnTime.Subtract(DateTime.Now);
            if (tmpTimeSpan.Ticks <= 0)
            {
                _noticeMe.Enabled = false;
                Transparent();
                isUp = true;
            }

            if (isUp)
            {
                ResetDisplayText();
            }
            else
            {
                UpdateText(string.Format("[{0}:{1:00}]", tmpTimeSpan.Minutes, tmpTimeSpan.Seconds));

                string speakString = _tdo.Text + " respawns " + tmpTimeSpan.Seconds + " seconds.";

                if ((int)tmpTimeSpan.TotalSeconds == _tdo.UrgentAlertSeconds)
                {

                    if (_noticeMe.Interval > 500)
                    {
                        _noticeMe.Interval = 500;
                        Blink();
                    }
                    //For future developmen
                    //if (!isSpeaking)
                    //{
                    //    isSpeaking = true;
                    //    if (tmpTimeSpan.Minutes > 0)
                    //        speakString = _defaultText + " respawns " + tmpTimeSpan.Minutes + " minutes and " + tmpTimeSpan.Seconds + " seconds.";
                    //    speaker.SpeakAsync(speakString);
                    //}

                }

                else if ((int)tmpTimeSpan.TotalSeconds == _tdo.AlertSeconds)
                {
                    if (_noticeMe.Interval < 1000)
                    {
                        _noticeMe.Interval = 1000;
                        Blink();
                    }
                    if (_noticeMe.Enabled == false)
                        _noticeMe.Enabled = true;
                    
                    //For future development
                    //if (!isSpeaking)
                    //{
                    //    isSpeaking = true;
                        
                    //    if(tmpTimeSpan.Minutes > 0)
                    //        speakString = _defaultText + " respawns " + tmpTimeSpan.Minutes + " minutes and " + tmpTimeSpan.Seconds + " seconds.";
                    //    speaker.SpeakAsync(speakString);
                    //}
                }
            }
        }

        //For future development
        //void speaker_SpeakCompleted(object sender, SpeakCompletedEventArgs e)
        //{
        //    isSpeaking = false;
        //}

        #region delegates

        delegate void SetTextCallback(string text);
        public void UpdateText(string text)
        {
            try
            {
                if (lblDisplayText.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(UpdateText);
                    this.Invoke(d, new object[] { text });
                }
                else
                {
                    lblDisplayText.Text = text;
                }
            }
            catch (Exception)
            {
                //Ignore the error completely
            }
        }

        delegate void SetResetTextCallback();
        public void ResetDisplayText()
        {
            try
            {
                if (lblDisplayText.InvokeRequired)
                {
                    SetResetTextCallback d = new SetResetTextCallback(ResetDisplayText);
                    this.Invoke(d, new object[] { });
                }
                else
                {
                    lblDisplayText.Text =_tdo.Text;
                }
            }
            catch (Exception)
            {
                //Ignore the error completely
            }
        }

        delegate void SetFontCallBack(SerializableFont font, Color foreColor);
        public void UpdateFont(SerializableFont font, Color foreColor)
        {

            try
            {
                if (lblDisplayText.InvokeRequired)
                {
                    SetFontCallBack d = new SetFontCallBack(UpdateFont);
                    this.Invoke(d, new object[] { font, foreColor });
                }
                else
                {
                    lblDisplayText.ForeColor = foreColor;
                    lblDisplayText.Font = font;
                }
            }
            catch (Exception ex)
            {
                //Ignore the error completely
            }
        }

        delegate void SetImageCallBack(Image image);
        public void UpdateImage(Image image)
        {
            if (pbImage.InvokeRequired)
            {
                SetImageCallBack d = new SetImageCallBack(UpdateImage);
                this.Invoke(d, new object[] { image });
            }
            else
            {
                pbImage.Image = image;
            }
        }

        delegate void SetResetSizeCallBack(float scale);
        public void ResetSize(float scale)
        {
            try
            {
                if (InvokeRequired)
                {
                    SetResetSizeCallBack d = new SetResetSizeCallBack(ResetSize);
                    this.Invoke(d, new object[] { scale });
                }
                else
                {
                    Bounds = new Rectangle(Bounds.X, Bounds.Y, (int)(_tdo.DisplayImage.Width * scale), (int)(_tdo.DisplayImage.Height * scale));
                }
            }
            catch (Exception ex)
            {
                //Ignore the error completely
            }
        }

        delegate void SetBlinkCallBack();
        public void Blink()
        {
            try
            {
                if (lblDisplayText.InvokeRequired)
                {
                    SetBlinkCallBack d = new SetBlinkCallBack(Blink);
                    this.Invoke(d, new object[] { });
                }
                else
                {
                    if (lblDisplayText.BackColor == _tdo.AlertColor)
                        lblDisplayText.BackColor = Color.Transparent;
                    else
                        lblDisplayText.BackColor = _tdo.AlertColor;
                }
            }
            catch (Exception ex)
            {
                //Ignore the error completely
            }
        }

        delegate void SetTransparentCallBack();
        public void Transparent()
        {
            try
            {
                if (lblDisplayText.InvokeRequired)
                {
                    SetTransparentCallBack d = new SetTransparentCallBack(Transparent);
                    this.Invoke(d, new object[] { });
                }
                else
                {

                    lblDisplayText.BackColor = Color.Transparent;

                }
            }
            catch (Exception ex)
            {
                //Ignore the error completely
            }
        }

        delegate void SetEnsureOnTopCallBack();
        public void EnsureOnTop()
        {
            try
            {
                if (InvokeRequired)
                {
                    SetEnsureOnTopCallBack d = new SetEnsureOnTopCallBack(EnsureOnTop);
                    this.Invoke(d, new object[] { });
                }
                else
                {
                    //if(!TopLevel)
                    TopLevel = true;
                    //if(!TopMost)
                    TopMost = true;
                }
            }
            catch (Exception ex)
            {
                //Ignore the error completely
            }
        }

        #endregion

        private void TimerDisplay_ResizeEnd(object sender, EventArgs e)
        {
            _tdo.Bounds = Bounds;
        }
    }
}
